package com.security.securitylogin;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
import com.security.pojo.User;
import com.security.service.UserService;

import java.util.Arrays;
import java.util.List;

/**
 * SpringSecurity需要使用的一个服务对象
 * SpringSecurity提供了一个接口，命名为UserDetailsService
 * 其中，定义了唯一的认证方法。命名为loadUserByUsername
 * 方法代表，用户认证
 * 抛出异常UsernameNotFoundException,当用户查询失败的时候，抛出
 *   用户查询，是只用用户名作为条件，查询用户
 * Spring Security内部自动匹配密码是否正确的时候，一定要进行加密及解密处理
 *   要求Spring容器中，必须存在一个PasswordEncoder实现类对象
 *   对象中，提供加密逻辑和解密逻辑。解密逻辑不是要求反向计算明文，而是要求
 *   实现明文和密文的验证。解密验证可以，加密验证亦可以
 * @USER: ysx
 * @CREATE: 2021/1/24 20:43
 */
@Component("MyUserDetailsService")
public class UserServiceImpl implements UserDetailsService {

    @Autowired
    private UserService userService;

    @Override
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        System.out.println("自定义用户认证逻辑" + username);
        User user = userService.login(username);
        System.out.println(user.getPassword());
        if (null == user) {
            //查询失败，用户名不存在。抛出异常
            throw new UsernameNotFoundException("用户名错误");
        }
        System.out.println("自定义用户认证逻辑，登录用户是：" + user);
        //查询成功，用户存在，需要匹配用户名密码是否正确
        //匹配密码，是由SpringSecurity内部逻辑自动完成。只需要把查询的用户名正确密码返回即可
        //返回结果是UserDetails类型的实现类，可以自定义，可以使用Security框架提供实现类
        //security框架提供的UserDetails接口实现类型User，构造的时候，需要提供3个参数，或者7个参数
        //3参数构造：用户身份（用户名）、用户正确密码（数据库中的密码），权限集合（Collection集合）
        //AuthorityUtils - 工具类，可以通过字符串，创建权限集合。

        //用户登录成功后，查询用户的权限集合。包括角色和权限
        //角色名称的集合
        List<String> rolesByUser = userService.findRolesByUser(user);

        //权限描述的集合
        List<String> resourcesByUser = userService.findResourcesByUser(user);
        //在Spring Security中，对角色的命名有严格的规则。要求角色名称的前缀必须是ROLE_。
        //不要再数据库中保存ROLE_角色，直接保存角色，代码中字符串拼接
        //在Spring Security中，角色和权限是平等的，都代表用户的访问权限
        //处理角色的名称问题

        String[] authorities = new String[rolesByUser.size() + resourcesByUser.size()];
        for (int i = 0; i < rolesByUser.size(); i++) {
            authorities[i] = "ROLE_" + rolesByUser.get(i);
        }

        for (int i = 0; i < resourcesByUser.size(); i++) {
            authorities[rolesByUser.size() + i] = resourcesByUser.get(i);
        }
        System.out.println("用户" + user.getUsername() + "的权限集合是：" + Arrays.toString(authorities));

        org.springframework.security.core.userdetails.User result =
                new org.springframework.security.core.userdetails.User(
                        username, user.getPassword(), AuthorityUtils.createAuthorityList(authorities)
                );
        return result;
    }
}
